using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using C5;
using CoreCalc.SheetDefinedFunctions;
using CoreCalc.SheetDefinedFunctions.CompOnlyNeededCells;

namespace CoreCalc.SheetDefinedFunctions
{
  // By 2008-11-11 everything in this file is obsolete and should be deleted.
  // Except that
  // the mysterious ConditionTree involves And and Or.  These dependencies should be discarded.

  public abstract class CGFunction : CodeGenerate
  {
    public readonly String name;

    public CGFunction(String name)
    {
      this.name = name.ToUpper();
    }

    public abstract void Compile(ILGenerator ilg, CGExpr[] expressions);

    public abstract void CompileToDouble(ILGenerator ilg, Generate g, CGExpr[] expressions);

    // The code generated by ifDouble expects a float64 on the stack top; 
    // the code generated by ifOther expects nothing on the stack top.
    public abstract void CompileToDouble(ILGenerator ilg, Generate ifDouble,
                                         Generate ifOther, CGExpr[] expressions);

    // General version.  Overridden in Comparison, IfExpression, Pi, RandomValue, And, Or, Not
    // The code generated by ifProper expects a finite non-NaN float64 on the stack top;
    // the code generated by ifOther expects nothing on the stack top.
    public virtual void CompileToDoubleProper(ILGenerator ilg, Generate ifProper,
                                              Generate ifOther, CGExpr[] expressions)
    {
      CompileToDouble(ilg,
        new Generate(delegate
      {
        ilg.Emit(OpCodes.Stloc, tmpDouble);
        ilg.Emit(OpCodes.Ldloc, tmpDouble);
        ilg.EmitCall(OpCodes.Call, isInfinityMethod, null);
        ilg.Emit(OpCodes.Brtrue, ifOther.GetLabel(ilg));
        ilg.Emit(OpCodes.Ldloc, tmpDouble);
        ilg.EmitCall(OpCodes.Call, isNaNMethod, null);
        ilg.Emit(OpCodes.Brtrue, ifOther.GetLabel(ilg));
        ilg.Emit(OpCodes.Ldloc, tmpDouble);
        ifProper.Gen(ilg);
        if (!ifOther.Generated)
        {
          Label endLabel = ilg.DefineLabel();
          ilg.Emit(OpCodes.Br, endLabel);
          ifOther.Gen(ilg);
          ilg.MarkLabel(endLabel);
        }
      }),
        ifOther,
        expressions);
    }

    // General version.  Overridden in Comparison, IfExpression, Pi, And, Or, Not
    public virtual void CompileCondition(ILGenerator ilg, Generate ifTrue, Generate ifFalse,
                                         Generate ifOther, CGExpr[] expressions)
    {
      CompileToDoubleProper(ilg,
        new Generate(delegate
      {
        ilg.Emit(OpCodes.Ldc_R8, 0.0);
        ilg.Emit(OpCodes.Beq, ifFalse.GetLabel(ilg));
        ifTrue.Gen(ilg);
        if (!ifFalse.Generated)
        {
          Label endLabel = ilg.DefineLabel();
          ilg.Emit(OpCodes.Br, endLabel);
          ifFalse.Gen(ilg);
          ilg.MarkLabel(endLabel);
        }
      }),
      ifOther,
      expressions);
    }

    internal abstract StrictCellTypeHolder GetStrictCells(CGExpr[] expressions);

    public virtual string ToString(CGExpr[] expressions)
    {
      return base.ToString();
    }

    public Typ GetInputTyp(int pos)
    {
      if (pos < 0 || pos >= Arity)
        throw new InvalidOperationException("Index out of bounds");
      return GetInputTypWithoutLengthCheck(pos);
    }

    // sestoft: We may want to change this to take an array Typ[] of argument types 
    public abstract Typ Type(CGExpr[] expressions);
    public abstract int Arity { get; }
    protected abstract Typ GetInputTypWithoutLengthCheck(int pos);

    internal abstract ConditionDictionary GetConditions(CGExpr[] expressions);
  }

  // ----------------------------------------------------------------

  public abstract class FixedArityOperation : CGFunction
  {
    public FixedArityOperation(String name) : base(name) { }

    internal override StrictCellTypeHolder GetStrictCells(CGExpr[] expressions)
    {
      if (expressions.Length == 0)
        return new StrictCellTypeHolder(false);

      StrictCellTypeHolder first = expressions[0].GetStrictCells();

      //todo: maybe some error checking on the indexes
      // sestoft: This seems to assign the "expected" type GetInputTyp(i) 
      // to each of the variables in the set expressions[i].GetStrictCells();
      // but is this really correct?  
      for (int i = 1; i < expressions.Length; i++)
      {
        // sestoft: Let's see if this makes any difference.  Turns out it makes 
        // GAUSSIAN and INVGAUSSIAN introduce a few extra ininst NumberValue tests.
        // first.Union(expressions[i].GetStrictCells(), Typ.NotApplicable);
        first.Union(expressions[i].GetStrictCells(), GetInputTyp(i));
      }

      return first;
    }
  }

  public abstract class UnitToNum : StrictFixedArityOperation
  {
    public UnitToNum(String name) : base(name) { }

    public override string ToString(CGExpr[] expressions)
    {
      if (expressions.Length == 0)
        return name + "()";
      else
        return "Err: ArgCount";
    }

    public override Typ Type(CGExpr[] expressions)
    {
      return Typ.Number;
    }

    protected override Typ GetInputTypWithoutLengthCheck(int pos)
    {
      throw new Exception("Impossible");
    }

    public override int Arity { get { return 0; } }
  }

  public abstract class NumToNum : StrictFixedArityOperation
  {
    public NumToNum(String name) : base(name) { } 

    protected abstract void GenOperation(ILGenerator ilg);

    public override void Compile(ILGenerator ilg, CGExpr[] expressions)
    {
      if (expressions.Length != 1)
        LoadStandardError(ilg, StandardError.ArgCount);
      else
      {
        CompileToDouble(ilg, new Generate(delegate { }),
                         new Generate(delegate { ilg.Emit(OpCodes.Ldc_R8, double.NaN); }),
                         expressions);
        WrapDoubleToNumberValue(ilg);
      }
    }

    public override void CompileToDouble(ILGenerator ilg, Generate g, CGExpr[] expressions)
    {
      CompileToDouble(ilg, g, GenLoadStandardError(ilg, StandardError.OtherError), expressions);
    }

    public override void CompileToDouble(ILGenerator ilg, Generate ifDouble, Generate ifOther, CGExpr[] expressions)
    {
      if (expressions.Length != 1)
        ifOther.Gen(ilg);
      else
      {
        expressions[0].CompileToDouble(ilg,
            new Generate(delegate
            {
              GenOperation(ilg);
              ifDouble.Gen(ilg);
            }),
            ifOther);
      }
    }

    public override string ToString(CGExpr[] expressions)
    {
      if (expressions.Length == 1)
        return name + "(" + expressions[0] + ")";
      else
        return "Err: ArgCount";
    }

    public override Typ Type(CGExpr[] expressions) { return Typ.Number; }

    protected override Typ GetInputTypWithoutLengthCheck(int pos)
    {
      return Typ.Number;
    }

    public override int Arity { get { return 1; } }
  }

  public abstract class StrictFixedArityOperation : FixedArityOperation
  {
    public StrictFixedArityOperation(String name) : base(name) { }

    // sestoft: It's this method that assumes strictness:
    internal override ConditionDictionary GetConditions(CGExpr[] expressions)
    {
      ConditionDictionary result = null;
      foreach (CGExpr expr in expressions)
      {
        if (result == null)
          result = expr.GetConditions();
        else
          result.Or(expr.GetConditions());
      }
      if (result != null)
        return result;
      else
        return new ConditionDictionary();
    }
  }

  public abstract class NumNumToNum : StrictFixedArityOperation
  {
    public NumNumToNum(String name) : base(name) { }

    protected abstract void GenOperation(ILGenerator ilg);

    public override void Compile(ILGenerator ilg, CGExpr[] expressions)
    {
      if (expressions.Length != 2)
        LoadStandardError(ilg, StandardError.ArgCount);
      else
        CompileToDouble(ilg, new Generate(delegate { WrapDoubleToNumberValue(ilg); }), expressions);
    }

    public override void CompileToDouble(ILGenerator ilg, Generate g, CGExpr[] expressions)
    {
      CompileToDouble(ilg, g, GenLoadStandardError(ilg, StandardError.NumError), expressions);
    }

    public override void CompileToDouble(ILGenerator ilg, Generate ifDouble,
                                         Generate ifOther, CGExpr[] expressions)
    {
      expressions[0].CompileToDouble(ilg,
        new Generate(delegate
            {
              expressions[1].CompileToDouble(ilg,
                new Generate(delegate
                  {
                    GenOperation(ilg);
                    ifDouble.Gen(ilg);
                  }),
                new Generate(delegate
                  {
                    ilg.Emit(OpCodes.Pop);
                    ifOther.Gen(ilg);
                  }));
            }),
        ifOther);
    }

    public override string ToString(CGExpr[] expressions)
    {
      if (expressions.Length == 2)
        return "(" + expressions[0] + name + expressions[1] + ")";
      else
        return "Err: ArgCount";
    }

    public override Typ Type(CGExpr[] expressions)
    {
      if (expressions.Length != 2)
        return Typ.Error;
      else
        return Typ.Number;
    }

    protected override Typ GetInputTypWithoutLengthCheck(int pos)
    {
      return Typ.Number;
    }

    public override int Arity { get { return 2; } }
  }

  public class ArithNumNumToNum : NumNumToNum
  {
    public readonly OpCode opCode;

    public ArithNumNumToNum(String name, OpCode opCode) : base(name) {
      this.opCode = opCode;
    }

    protected override void GenOperation(ILGenerator ilg)
    {
      ilg.Emit(opCode);
    }
  }

  // This implementation of AND is non-strict in its second argument 
  // although that's not Excel semantics.
  public class And : FixedArityOperation
  {
    public static readonly And instance = new And();

    protected And() : base("AND") { }

    public override void CompileToDouble(ILGenerator ilg, Generate g, CGExpr[] expressions)
    {
      CompileToDouble(ilg, g, GenLoadStandardError(ilg, StandardError.OtherError), expressions);
    }

    public override void Compile(ILGenerator ilg, CGExpr[] expressions)
    {
      CompileToDouble(ilg,
        new Generate(delegate
        {
          WrapDoubleToNumberValue(ilg);
        }),
        new Generate(delegate
        {
          LoadStandardError(ilg, StandardError.ArgType);
        }),
        expressions);
    }

    public override void CompileToDouble(ILGenerator ilg,
                                         Generate ifDouble, Generate ifOther, CGExpr[] expressions)
    {
      // If the result of AND(e0,e1) is not proper, then the result of AND(e0,e1) is not a number
      CompileToDoubleProper(ilg, ifDouble, ifOther, expressions);
    }

    public override void CompileToDoubleProper(ILGenerator ilg,
                                               Generate ifProper, Generate ifOther, CGExpr[] expressions)
    {
      // If e0 is a proper non-zero number, then AND(e0,e1) is the proper value of e1
      expressions[0].CompileCondition(ilg,
        new Generate(delegate
        {
          expressions[1].CompileToDoubleProper(ilg, ifProper, ifOther);
        }),
        new Generate(delegate
        {
          ilg.Emit(OpCodes.Ldc_R8, 0.0);
          ifProper.Gen(ilg);
        }),
        ifOther);
    }

    public override void CompileCondition(ILGenerator ilg, Generate ifTrue,
                                 Generate ifFalse, Generate ifOther, CGExpr[] expressions)
    {
      // Using AND(e0,e1) as a condition, it is true if e0 is true and e1 is true; otherwise false
      expressions[0].CompileCondition(ilg,
        new Generate(delegate
        {
          expressions[1].CompileCondition(ilg, ifTrue, ifFalse, ifOther);
        }),
        ifFalse,
        ifOther);
    }
  
    public override string ToString(CGExpr[] expressions)
    {
      if (expressions.Length == 2)
        return "AND(" + expressions[0] + ", " + expressions[1] + ")";
      else
        return "Err: AND ArgCount";
    }

    internal override StrictCellTypeHolder GetStrictCells(CGExpr[] expressions)
    {
      // sestoft: strict only in first argument (if I understand correctly)
      return expressions[0].GetStrictCells();
    }

    public override Typ Type(CGExpr[] expressions)
    {
      if (expressions.Length != 2)
        return Typ.Error;
      else
        return Typ.Number;
    }

    protected override Typ GetInputTypWithoutLengthCheck(int pos)
    {
      return Typ.Number;
    }

    public override int Arity { get { return 2; } }

    internal override ConditionDictionary GetConditions(CGExpr[] expressions)
    {
      if (expressions.Length != 2)
        return new ConditionDictionary();
      else
      {
        // The second expression's references are needed only if the first 
        // expression evaluates to true
        ConditionDictionary dic1 = expressions[1].GetConditions();
        dic1.And(new Condition(expressions[0], true));
        ConditionDictionary dic0 = expressions[0].GetConditions();
        dic1.Or(dic0);
        return dic1;
      }
    }
  }

  public class Or : FixedArityOperation
  {
    public static readonly Or instance = new Or();

    protected Or() : base("OR") { }

    public override void CompileToDouble(ILGenerator ilg, Generate g, CGExpr[] expressions)
    {
      CompileToDouble(ilg, g, GenLoadStandardError(ilg, StandardError.OtherError), expressions);
    }

    public override void Compile(ILGenerator ilg, CGExpr[] expressions)
    {
      CompileToDouble(ilg,
        new Generate(delegate
        {
          WrapDoubleToNumberValue(ilg);
        }),
        new Generate(delegate
        {
          LoadStandardError(ilg, StandardError.ArgType);
        }),
        expressions);
    }

    public override void CompileToDouble(ILGenerator ilg,
                                         Generate ifDouble, Generate ifOther, CGExpr[] expressions)
    {
      // If the result of OR(e0,e1) is not proper, then the result of OR(e0,e1) is not a number value
      CompileToDoubleProper(ilg, ifDouble, ifOther, expressions);
    }

    public override void CompileToDoubleProper(ILGenerator ilg,
                                               Generate ifProper, Generate ifOther, CGExpr[] expressions)
    {
      // If e0 is proper and zero number, then OR(e0,e1) is the proper value of e1
      expressions[0].CompileCondition(ilg,
        new Generate(delegate
        {
          ilg.Emit(OpCodes.Ldc_R8, 1.0);
          ifProper.Gen(ilg);
        }),
        new Generate(delegate
        {
          expressions[1].CompileToDoubleProper(ilg, ifProper, ifOther);
        }),
        ifOther);
    }

    public override void CompileCondition(ILGenerator ilg, Generate ifTrue,
                                 Generate ifFalse, Generate ifOther, CGExpr[] expressions)
    {
      // Using OR(e0,e1) as a condition, it is false if e0 is false and e1 is false; otherwise true
      expressions[0].CompileCondition(ilg,
        ifTrue,
        new Generate(delegate
        {
          expressions[1].CompileCondition(ilg, ifTrue, ifFalse, ifOther);
        }),
        ifOther);
    }

    public override string ToString(CGExpr[] expressions)
    {
      if (expressions.Length == 2)
        return "OR(" + expressions[0] + ", " + expressions[1] + ")";
      else
        return "Err: OR ArgCount";
    }

    internal override StrictCellTypeHolder GetStrictCells(CGExpr[] expressions)
    {
      // sestoft: strict only in first argument (if I understand correctly)
      return expressions[0].GetStrictCells();
    }

    public override Typ Type(CGExpr[] expressions)
    {
      if (expressions.Length != 2)
        return Typ.Error;
      else
        return Typ.Number;
    }

    protected override Typ GetInputTypWithoutLengthCheck(int pos)
    {
      return Typ.Number;
    }

    public override int Arity { get { return 2; } }

    internal override ConditionDictionary GetConditions(CGExpr[] expressions)
    {
      if (expressions.Length != 2)
        return new ConditionDictionary();
      else
      {
        // The second expression's references are needed only if the first 
        // expression evaluates to false
        ConditionDictionary dic1 = expressions[1].GetConditions();
        dic1.And(new Condition(expressions[0], false));
        ConditionDictionary dic0 = expressions[0].GetConditions();
        dic1.Or(dic0);
        return dic1;
      }
    }
  }

  // sestoft: This should also work for strings etc.  Static type
  // information should be exploited when available, and when not, 
  // a generic comparer should be generated.  For doubles, need to 
  // see whether we're consistent with respect to unordered comparison.
  public class GreaterThan : Comparison
  {
    public static readonly GreaterThan instance = new GreaterThan();

    private GreaterThan() : base(">") { }

    protected override void GenCompareDouble(ILGenerator ilg)
    {
      ilg.Emit(OpCodes.Cgt);
    }

    protected override void GenDoubleFalseJump(ILGenerator ilg, Label target)
    {
      ilg.Emit(OpCodes.Ble, target);
    }
  }

  public class LessThan : Comparison
  {
    public static readonly LessThan instance = new LessThan();

    private LessThan() : base("<") { }

    protected override void GenCompareDouble(ILGenerator ilg)
    {
      ilg.Emit(OpCodes.Clt);
    }

    protected override void GenDoubleFalseJump(ILGenerator ilg, Label target)
    {
      ilg.Emit(OpCodes.Bge, target);
    }
  }

  public class LessThanOrEqual : Comparison
  {
    public static readonly LessThanOrEqual instance = new LessThanOrEqual();

    private LessThanOrEqual() : base("<=") { }

    protected override void GenCompareDouble(ILGenerator ilg)
    {
      ilg.Emit(OpCodes.Cgt);
      ilg.Emit(OpCodes.Not);
    }

    protected override void GenDoubleFalseJump(ILGenerator ilg, Label target)
    {
      ilg.Emit(OpCodes.Bgt, target);
    }
  }

  public class GreaterThanOrEqual : Comparison
  {
    public static readonly GreaterThanOrEqual instance = new GreaterThanOrEqual();

    private GreaterThanOrEqual() : base(">=") { }

    protected override void GenCompareDouble(ILGenerator ilg)
    {
      ilg.Emit(OpCodes.Clt);
      ilg.Emit(OpCodes.Not);
    }

    protected override void GenDoubleFalseJump(ILGenerator ilg, Label target)
    {
      ilg.Emit(OpCodes.Blt, target);
    }
  }

  public class Equal : Comparison
  {
    public static readonly Equal instance = new Equal();

    private Equal() : base("==") { }

    protected override void GenCompareDouble(ILGenerator ilg)
    {
      ilg.Emit(OpCodes.Ceq);
    }

    protected override void GenDoubleFalseJump(ILGenerator ilg, Label target)
    {
      ilg.Emit(OpCodes.Bne_Un, target);
    }
  }

  public class NotEqual : Comparison
  {
    public static readonly NotEqual instance = new NotEqual();

    private NotEqual() : base("<>") { }

    protected override void GenCompareDouble(ILGenerator ilg)
    {
      ilg.Emit(OpCodes.Ceq);
      ilg.Emit(OpCodes.Not);
    }

    protected override void GenDoubleFalseJump(ILGenerator ilg, Label target)
    {
      ilg.Emit(OpCodes.Beq, target);
    }
  }

  /// <summary>
  /// A comparison takes two operands, both numeric for now, and produces
  /// the outcome true (1.0) or false (0.0), or in case either operand 
  /// evaluates to NaN or +/-infinity, it produces the outcome other (NaN).
  /// </summary>
  public abstract class Comparison : StrictFixedArityOperation
  {
    public Comparison(String name) : base(name) { }

    // These are used in a template method pattern
    // Emit instruction to compare doubles, leaving 1 on stack if true:
    protected abstract void GenCompareDouble(ILGenerator ilg);
    // Emit instructions to compare doubles, jump to target if false:
    protected abstract void GenDoubleFalseJump(ILGenerator ilg, Label target);

    public override void Compile(ILGenerator ilg, CGExpr[] expressions)
    {
      if (expressions.Length != 2)
        LoadStandardError(ilg, StandardError.ArgCount);
      else
      {
        expressions[0].Compile(ilg);
        UnwrapToDouble(ilg, new Generate(delegate
        {
          expressions[1].Compile(ilg);
          UnwrapToDouble(ilg, new Generate(delegate
          {
            GenCompareDouble(ilg);
            // sestoft: this does not seem to be needed:
            // ilg.Emit(OpCodes.Conv_R8);
            WrapDoubleToNumberValue(ilg);
          }),
           GenPopLoadStandardError(ilg, StandardError.NumError));
        }),
        GenLoadStandardError(ilg, StandardError.NumError));
      }
    }

    public override void CompileToDouble(ILGenerator ilg, Generate g, CGExpr[] expressions)
    {
      CompileToDouble(ilg, g, GenLoadStandardError(ilg, StandardError.OtherError), expressions);
    }

    // This override evaluates both sides of the comparisons to proper doubles,
    // performs the comparison and then ifDouble.Gen(); or else generates an 
    // ErrorValue and then ifOther.Gen()
    public override void CompileToDouble(ILGenerator ilg, Generate ifDouble,
                                         Generate ifOther, CGExpr[] expressions)
    {
      expressions[0].CompileToDoubleProper(ilg,
        new Generate(delegate
        {
          expressions[1].CompileToDoubleProper(ilg,
             new Generate(delegate
             {
               GenCompareDouble(ilg);
               ifDouble.Gen(ilg);
             }),
             new Generate(delegate
             {
               ilg.Emit(OpCodes.Pop);
               // sestoft: is this correct??
               LoadStandardError(ilg, StandardError.ArgType);
               ifOther.Gen(ilg);
             }));
        }),
        new Generate(delegate
      {
        // sestoft: is this correct??
        LoadStandardError(ilg, StandardError.ArgType);
        ifOther.Gen(ilg);
      }));
    }

    public override void CompileToDoubleProper(ILGenerator ilg, Generate ifProper, Generate ifOther, CGExpr[] expressions)
    {
      expressions[0].CompileToDoubleProper(ilg,
        new Generate(delegate
        {
          expressions[1].CompileToDoubleProper(ilg,
             new Generate(delegate
             {
               GenCompareDouble(ilg);
               ifProper.Gen(ilg);
             }),
             new Generate(delegate
             {
               ilg.Emit(OpCodes.Pop);
               ifOther.Gen(ilg);
             }));
        }),
        ifOther);
    }

    // This override combines the ordering predicate and the conditional jump
    public override void CompileCondition(ILGenerator ilg, Generate ifTrue,
                                          Generate ifFalse, Generate ifOther,
                                          CGExpr[] expressions)
    {
      expressions[0].CompileToDoubleProper(ilg,
        new Generate(delegate
        {
          expressions[1].CompileToDoubleProper(ilg,
            new Generate(delegate
            {
              GenDoubleFalseJump(ilg, ifFalse.GetLabel(ilg));
              ifTrue.Gen(ilg);
              if (!ifFalse.Generated)
              {
                Label endLabel = ilg.DefineLabel();
                ilg.Emit(OpCodes.Br, endLabel);
                ifFalse.Gen(ilg);
                ilg.MarkLabel(endLabel);
              }
            }),
            new Generate(delegate
            {
              ilg.Emit(OpCodes.Pop);
              ifOther.Gen(ilg);
            }));
        }),
        ifOther);
    }


    public override string ToString(CGExpr[] expressions)
    {
      if (expressions.Length == 2)
        return expressions[0] + name + expressions[1];
      else
        return "Err: ArgCount";
    }

    public override Typ Type(CGExpr[] expressions)
    {
      return Typ.Number;
    }

    protected override Typ GetInputTypWithoutLengthCheck(int pos)
    {
      return Typ.Number;
    }

    public override int Arity { get { return 2; } }
  }

  public class Pi : UnitToNum
  {
    public static readonly Pi instance = new Pi();

    private Pi() : base("PI") { }

    public override void Compile(ILGenerator ilg, CGExpr[] expressions)
    {
      if (expressions.Length != 0)
        LoadStandardError(ilg, StandardError.ArgCount);
      else
      {
        ilg.Emit(OpCodes.Ldc_R8, Math.PI);
        WrapDoubleToNumberValue(ilg);
      }
    }

    public override void CompileToDouble(ILGenerator ilg, Generate g, CGExpr[] expressions)
    {
      CompileToDouble(ilg, g, GenLoadStandardError(ilg, StandardError.ArgCount), expressions);
    }

    public override void CompileToDouble(ILGenerator ilg, Generate ifDouble,
                                         Generate ifOther, CGExpr[] expressions)
    {
      if (expressions.Length != 0)
        LoadStandardError(ilg, StandardError.ArgCount);
      else
      {
        ilg.Emit(OpCodes.Ldc_R8, Math.PI);
        ifDouble.Gen(ilg);
      }
    }

    public override void CompileToDoubleProper(ILGenerator ilg, Generate ifProper,
                                               Generate ifOther, CGExpr[] expressions)
    {
      CompileToDouble(ilg, ifProper, ifOther, expressions);  // Pi is always proper
    }

    public override void CompileCondition(ILGenerator ilg, Generate ifTrue,
                                          Generate ifFalse, Generate ifOther,
                                          CGExpr[] expressions)
    {
      ifTrue.Gen(ilg);  // Pi is non-zero
    }
  }

  public class RandomValue : UnitToNum
  {
    public static readonly RandomValue instance = new RandomValue();
    private static readonly MethodInfo nextDoubleMethod
      = typeof(System.Random).GetMethod("NextDouble", new Type[] { });

    private RandomValue() : base("RAND") { }

    public override void Compile(ILGenerator ilg, CGExpr[] expressions)
    {
      CompileToDouble(ilg, new Generate(delegate { WrapDoubleToNumberValue(ilg); }), expressions);
    }

    public override void CompileToDouble(ILGenerator ilg, Generate g, CGExpr[] expressions)
    {
      CompileToDouble(ilg, g, GenLoadStandardError(ilg, StandardError.ArgCount), expressions);
    }

    public override void CompileToDouble(ILGenerator ilg, Generate ifDouble,
                                         Generate ifOther, CGExpr[] expressions)
    {
      if (expressions.Length != 0)
        ifOther.Gen(ilg);
      else
      {
        ilg.Emit(OpCodes.Ldsfld, Function.randomField);
        ilg.EmitCall(OpCodes.Callvirt, nextDoubleMethod, null);
        ifDouble.Gen(ilg);
      }
    }

    public override void CompileToDoubleProper(ILGenerator ilg, Generate ifProper,
                                               Generate ifOther, CGExpr[] expressions)
    {
      CompileToDouble(ilg, ifProper, ifOther, expressions); // Random values are always proper
    }

    protected override Typ GetInputTypWithoutLengthCheck(int pos)
    {
      return Typ.NotApplicable; //kommer aldrig herned alligevel
    }

    internal override StrictCellTypeHolder GetStrictCells(CGExpr[] expressions)
    {
      return new StrictCellTypeHolder(false);
    }

    internal override ConditionDictionary GetConditions(CGExpr[] expressions)
    {
      return new ConditionDictionary();
    }
  }

  public class IfExpression : FixedArityOperation
  {
    public static readonly IfExpression instance = new IfExpression();

    private IfExpression() : base("IF") { }

    public override void CompileToDouble(ILGenerator ilg, Generate g, CGExpr[] expressions)
    {
      CompileToDouble(ilg, g, GenLoadStandardError(ilg, StandardError.OtherError), expressions);
    }

    public override void Compile(ILGenerator ilg, CGExpr[] expressions)
    {
      expressions[0].CompileCondition(ilg,
        new Generate(delegate
        {
          expressions[1].Compile(ilg);
        }),
        new Generate(delegate
        {
          expressions[2].Compile(ilg);
        }),
        new Generate(delegate
        {
          LoadStandardError(ilg, StandardError.ArgType);
        }));
    }

    public override void CompileToDouble(ILGenerator ilg,
                                         Generate ifDouble, Generate ifOther, CGExpr[] expressions)
    {
      expressions[0].CompileCondition(ilg,
        new Generate(delegate
        {
          expressions[1].CompileToDouble(ilg, ifDouble, ifOther);
        }),
        new Generate(delegate
        {
          expressions[2].CompileToDouble(ilg, ifDouble, ifOther);
        }),
        ifOther);
    }

    public override void CompileToDoubleProper(ILGenerator ilg,
                                               Generate ifProper, Generate ifOther, CGExpr[] expressions)
    {
      expressions[0].CompileCondition(ilg,
        new Generate(delegate
        {
          expressions[1].CompileToDoubleProper(ilg, ifProper, ifOther);
        }),
        new Generate(delegate
        {
          expressions[2].CompileToDoubleProper(ilg, ifProper, ifOther);
        }),
        ifOther);
    }

    // This handles compilation of IF(IF(e00,e01,e02), e1, e2) 
    public override void CompileCondition(ILGenerator ilg, Generate ifTrue,
                                          Generate ifFalse, Generate ifOther, CGExpr[] expressions)
    {
      expressions[0].CompileCondition(ilg,
        new Generate(delegate
        {
          expressions[1].CompileCondition(ilg, ifTrue, ifFalse, ifOther);
        }),
        new Generate(delegate
        {
          expressions[2].CompileCondition(ilg, ifTrue, ifFalse, ifOther);
        }),
        ifOther);
    }

    // sestoft: used only in the suspect Mark IV code
    private static void CompToDouble(ILGenerator ilg, CGExpr fstExpression, Generate onSuccess, Generate onError)
    {
      LocalBuilder var;
      if (ConditionResults.TryGetValue(fstExpression, out var)) //MarkIV
      {
        //If the condition made a error it will lay NaN instead of true or false
        //therefore we only check for this
        Label error = ilg.DefineLabel();
        Label end = ilg.DefineLabel();
        ilg.Emit(OpCodes.Ldloc, var);
        ilg.Emit(OpCodes.Dup);
        ilg.Emit(OpCodes.Ldc_R8, double.NaN);
        ilg.Emit(OpCodes.Ceq);
        ilg.Emit(OpCodes.Brtrue, error);
        onSuccess.Gen(ilg);
        ilg.Emit(OpCodes.Br, end);
        ilg.MarkLabel(error);
        ilg.Emit(OpCodes.Pop);
        onError.Gen(ilg);
        ilg.MarkLabel(end);
        // sestoft 2008-10-02: Looks suspicious -- does it mean a condition can only be tested once:
        // ... and it actually seems to introduce errors (premature reuse of localvars) in
        // the MarkIV Gaussian distribution function.  But it is not the only error, commenting
        // it out still generates illegal bytecode
        doubleVarPool.ReleaseVariable(var);
      }
      else
        fstExpression.CompileToDouble(ilg, onSuccess, onError);
    }

    public override string ToString(CGExpr[] expressions)
    {
      if (expressions.Length != 0)
        return expressions[0] + " ? " + expressions[1] + " : " + expressions[2];
      else
        return "Err: ArgCount";
    }

    internal override StrictCellTypeHolder GetStrictCells(CGExpr[] expressions)
    {
      StrictCellTypeHolder back = expressions[1].GetStrictCells();
      // sestoft: Seems wrong, this should be Intersection, not Union:
      back.Union(expressions[2].GetStrictCells());
      // sestoft: What's this:
      back.InsertAllAsNonResults(expressions[0].GetStrictCells());
      return back;
    }

    public override Typ Type(CGExpr[] expressions)
    {
      if (expressions.Length != 3)
        return Typ.Error;
      else
        return Lub(expressions[1].Type(), expressions[2].Type());
    }

    protected override Typ GetInputTypWithoutLengthCheck(int pos)
    {
      if (pos == 0)
        return Typ.Number;
      else
        return Typ.Value;
    }

    public override int Arity { get { return 3; } }

    internal override ConditionDictionary GetConditions(CGExpr[] expressions)
    {
      if (expressions.Length != 3)
        return new ConditionDictionary();
      else
      {
        //The two last expressions references are dependend on the outcome of the first
        ConditionDictionary dic1 = expressions[1].GetConditions();
        dic1.And(new Condition(expressions[0], true));

        ConditionDictionary dic2 = expressions[2].GetConditions();
        dic2.And(new Condition(expressions[0], false));

        dic1.Or(dic2);

        //The first expressions references are not dependend on anything
        ConditionDictionary dic0 = expressions[0].GetConditions();
        dic1.Or(dic0);

        return dic1;
      }
    }
  }

  //public class Sum : VariableArityOperation
  //{
  //    private Generate onError;
  //    private Generate onSuccess;

  //    public override bool IsVolatile
  //    {
  //        get { return false; }
  //    }

  //    public static readonly Sum instance = new Sum();
  //    public new const string NAME = "SUM";

  //    private Sum(){}

  //    private void Recursive(ILGenerator ilg, int i, CGExpr[] expressions)
  //    {
  //        expressions[i].Compile(ilg);
  //        UnwrapToDouble(ilg, new Generate(delegate
  //        {
  //            ilg.Emit(OpCodes.Add);

  //            i++;
  //            if (i == expressions.Length)
  //            {
  //               WrapDoubleToNumberValue(ilg);
  //            }
  //            else
  //            {
  //                Recursive(ilg, i, expressions);
  //            }
  //        }), GenPopLoadStandardError(ilg, StandardError.NumError));
  //    }
  //    //TODO: refactor to same 
  //    private void RecursiveToDouble(ILGenerator ilg, int i, CGExpr[] expressions)
  //    {
  //        expressions[i].CompileToDouble(ilg, new Generate(delegate
  //        {
  //            ilg.Emit(OpCodes.Add);

  //            i++;
  //            if (i == expressions.Length)
  //            {
  //                onSuccess.Gen(ilg);
  //            }
  //            else
  //            {
  //                RecursiveToDouble(ilg, i, expressions);
  //            }
  //        }), new Generate(delegate
  //               {
  //                   ilg.Emit(OpCodes.Pop);
  //                   onError.Gen(ilg);
  //               }));
  //    }

  //    //todo: expression must not be empty, check this somewhere else
  //    public override void Compile(ILGenerator ilg, CGExpr[] expressions)
  //    {
  //       CompileToDouble(ilg, new Generate(delegate() { WrapDoubleToNumberValue(ilg); }), expressions);
  //    }

  //    public override void CompileToDouble(ILGenerator ilg, Generate g, CGExpr[] expressions)
  //    {
  //        CompileToDoubleSpecialError(ilg, g, GenLoadStandardError(ilg, StandardError.ArgType), expressions);
  //    }

  //    public override void CompileToDoubleSpecialError(ILGenerator ilg, Generate onSuccess, Generate onError,
  //                                                CGExpr[] expressions)
  //    {
  //        // sestoft: Not clear this is correct
  //        this.onError = onError;
  //        this.onSuccess = new Generate(delegate { SkipIfNaNOrInf(ilg, onSuccess, this.onError); });
  //        ilg.Emit(OpCodes.Ldc_R8, 0.0);
  //        RecursiveToDouble(ilg, 0, expressions);

  //    }

  //    public override string ToString(CGExpr[] expressions)
  //    {
  //        string s = NAME+"(";
  //        foreach (CGExpr e in expressions)
  //        {
  //            s += e.ToString() + ";"; //todo: remove the last ";"
  //        }
  //        return s + ")";
  //    }

  //    internal override StrictCellTypeHolder GetStrictCells(CGExpr[] expressions)
  //    {
  //        if (expressions.Length == 0)
  //            return new StrictCellTypeHolder(false);

  //        StrictCellTypeHolder first = expressions[0].GetStrictCells();

  //        for (int i = 1; i < expressions.Length; i++)
  //        {
  //            first.Union(expressions[i].GetStrictCells(), Typ.Number);
  //        }

  //        return first;
  //    }

  //    public override Typ GetReturnTyp(CGExpr[] expressions){ return Typ.Number; }

  //    protected override Typ GetInputTypWithoutLengthCheck(int pos)
  //    {
  //        return Typ.Number;
  //    }

  //    public override int GetLength()
  //    {
  //        return int.MaxValue; //maxvalue is our representation of infinity... :)
  //    }

  //    internal override ConditionDictionary GetConditions(CGExpr[] expressions)
  //    {
  //        ConditionDictionary result = null;
  //        foreach (CGExpr expr in expressions)
  //        {
  //            if (result == null)
  //                result = expr.GetConditions();
  //            else
  //                result.Or(expr.GetConditions());
  //        }
  //        if(result != null)
  //            return result;
  //        else 
  //            return new ConditionDictionary();
  //    }
  //}

  //todo: could be simpler, e.g. only pass the CGMethodInfo object to the base class

  /// <summary>
  /// For static double -> double functions in class Math
  /// </summary>
  public class MathNumToNum : NumToNum
  {
    private readonly MethodInfo mi;

    public MathNumToNum(String name) : base(name)
    {
      this.mi = typeof(Math).GetMethod(name, new Type[] { typeof(double) });
    }

    protected override void GenOperation(ILGenerator ilg)
    {
      ilg.EmitCall(OpCodes.Call, mi, null);
    }
  }

  class VariousNumNumToNum : NumNumToNum
  {
    private readonly MethodInfo mi;

    public VariousNumNumToNum(String name, MethodInfo mi) : base(name)
    {
      this.mi = mi;
    }

    protected override void GenOperation(ILGenerator ilg)
    {
      ilg.EmitCall(OpCodes.Call, mi, null);
    }
  }

  #region OutCommented Matrix functions
  //class Transpose : CGFunction
  //{
  //    public const string NAME = "TRANSPOSE";

  //    public static FunctionCreator GetFunction()
  //    {
  //        return delegate(CGExpr[] expressions)
  //        {
  //            if (expressions.Length == 1)
  //                return new Transpose(expressions);
  //            else
  //                return new ErrorFunction(StandardError.ArgCount);
  //        };
  //    }

  //    public Transpose(CGExpr[] expressions)
  //        : base(expressions)
  //    { }

  //    public override void Compile(ILGenerator ilg, CGExpr[] expressions)
  //    {
  //        //enable this after testing... it should leave a MatrixValue on the stack, else and errorvalue will be returned
  //        //LocalBuilder inputMatrix = ilg.DeclareLocal(typeof(MatrixValue));
  //        //expressions[0].Compile(ilg);

  //        //TEST start
  //        LocalBuilder inputMatrix = ilg.DeclareLocal(typeof(MatrixValue));
  //        CreateMultiDimArray(ilg, typeof(Value[,]), 5, 2);
  //        ilg.Emit(OpCodes.Newobj, typeof(MatrixValue).GetConstructor(new Type[] { typeof(Value[,]) }));
  //        ilg.Emit(OpCodes.Stloc, inputMatrix);
  //        ilg.Emit(OpCodes.Ldloc, inputMatrix);

  //        //ilg.Emit(OpCodes.Ldc_R8, 2.2);
  //        //ilg.Emit(OpCodes.Newobj, typeof(NumberValue).GetConstructor(new Type[] { typeof(double) }));
  //        //TEST end

  //        CheckType(ilg, typeof (MatrixValue), delegate
  //                                                 {
  //                                                     TransposeFunc(ilg, typeof (Value[,]), typeof (Value), inputMatrix);
  //                                                     need to wrap as a MatrixValue
  //                                                     ilg.Emit(OpCodes.Newobj, typeof(MatrixValue).GetConstructor(new Type[] { typeof(Value[,]) }));

  //                                                 }, delegate
  //                                                        {
  //                                                            CreateStandardError(ilg, StandardError.ArgType);
  //                                                        });
  //    }

  //    public override void CompileToDouble(ILGenerator ilg, Generate g, CGExpr[] expressions)
  //    {
  //        throw new Exception("The method or operation is not implemented.");
  //    }

  //    needs col + row from input matrix
  //    private void TransposeFunc(ILGenerator ilg, Type arrayType, Type contentType, LocalBuilder inputMatrix)
  //    {
  //        LocalBuilder outputArray = ilg.DeclareLocal(arrayType);
  //        LocalBuilder inputCols = ilg.DeclareLocal(typeof (int));
  //        LocalBuilder inputRows = ilg.DeclareLocal(typeof(int));

  //        ilg.Emit(OpCodes.Callvirt, typeof(MatrixValue).GetMethod("get_Rows", new Type[] { }, null));
  //        ilg.Emit(OpCodes.Stloc, inputRows);
  //        ilg.Emit(OpCodes.Ldloc, inputMatrix); //need it again since it is off the stack
  //        ilg.Emit(OpCodes.Callvirt, typeof(MatrixValue).GetMethod("get_Cols", new Type[] { }, null));
  //        ilg.Emit(OpCodes.Stloc, inputCols);

  //        //col / row is reversed so we create a new transposed matrix
  //        ilg.Emit(OpCodes.Ldloc, inputRows);
  //        ilg.Emit(OpCodes.Ldloc, inputCols);
  //        CreateMultiDimArray(ilg, arrayType);
  //        ilg.Emit(OpCodes.Stloc, outputArray);

  //        LocalBuilder c = ilg.DeclareLocal(typeof(int));
  //        LocalBuilder r = ilg.DeclareLocal(typeof(int));
  //        LocalBuilder tmpValue = ilg.DeclareLocal(contentType);

  //        ForLoop(ilg, delegate
  //         {
  //             //reset counter
  //             ilg.Emit(OpCodes.Ldc_I4_0);
  //             ilg.Emit(OpCodes.Stloc, c);
  //             ForLoop(ilg, delegate
  //                  {
  //                      ilg.Emit(OpCodes.Ldloc, inputMatrix);
  //                      ilg.Emit(OpCodes.Ldloc, c);
  //                      ilg.Emit(OpCodes.Ldloc, r);
  //                      ilg.Emit(OpCodes.Call, typeof(MatrixValue).GetMethod("get_Item", new Type[] { typeof(int), typeof(int) }, null));
  //                      ilg.Emit(OpCodes.Stloc, tmpValue);

  //                      NOTICE: this is just a reference copy, could have dire consequences later on 
  //                      (atm i can not see that it would cause any problems since Value objects can
  //                      not be changed inside a MatrixValue unless one has a ref directly to the internal array)
  //                      ilg.Emit(OpCodes.Ldloc, outputArray);
  //                      ilg.Emit(OpCodes.Ldloc, r);
  //                      ilg.Emit(OpCodes.Ldloc, c);
  //                      ilg.Emit(OpCodes.Ldloc, tmpValue);
  //                      ilg.Emit(OpCodes.Call, arrayType.GetMethod("Set", new Type[] { typeof(int), typeof(int), contentType }, null));
  //                  }, c, delegate { ilg.Emit(OpCodes.Ldloc, inputCols); }, delegate { ilg.Emit(OpCodes.Ldc_I4_1); });
  //         }, r, delegate { ilg.Emit(OpCodes.Ldloc, inputRows); }, delegate { ilg.Emit(OpCodes.Ldc_I4_1); });

  //        ilg.Emit(OpCodes.Ldloc, outputArray);
  //    }

  //    internal override StrictCellTypeHolder GetStrictCells(CGExpr[] expressions)
  //    {
  //        throw new System.NotImplementedException();
  //    }

  //    public override string ToString(CGExpr[] expressions)
  //    {
  //        string s = NAME + "(";
  //        foreach (CGExpr e in expressions)
  //        {
  //            s += e.ToString() + ";"; //todo: remove the last ";"
  //        }
  //        return s + ")";
  //    }
  //}
  #endregion
}
